home *** CD-ROM | disk | FTP | other *** search
- #define LIBQBUILD_CORE
- #include "../include/libqbuild.h"
-
- int leaffaces; // 4
- int nodefaces; // 4
- int splitnodes; // 4
-
- int c_solid, c_empty, c_water; // 12
-
- bool usemidsplit; // ?
-
- //============================================================================
-
- /*
- * ==================
- * FaceSide
- *
- * For BSP hueristic
- * ==================
- */
- int FaceSide(register struct visfacet * in, register struct plane * split)
- {
- int frontcount, backcount;
- vec_t dot;
- int i;
- vec_t *p;
-
- frontcount = backcount = 0;
-
- // axial planes are fast
- if (split->type < 3)
- for (i = 0, p = in->pts[0] + split->type; i < in->numpoints; i++, p += 3) {
- if (*p > split->dist + ON_EPSILON) {
- if (backcount)
- return SIDE_ON;
- frontcount = 1;
- }
- else if (*p < split->dist - ON_EPSILON) {
- if (frontcount)
- return SIDE_ON;
- backcount = 1;
- }
- }
- else
- // sloping planes take longer
- for (i = 0, p = in->pts[0]; i < in->numpoints; i++, p += 3) {
- dot = DotProduct(p, split->normal);
- dot -= split->dist;
- if (dot > ON_EPSILON) {
- if (backcount)
- return SIDE_ON;
- frontcount = 1;
- }
- else if (dot < -ON_EPSILON) {
- if (frontcount)
- return SIDE_ON;
- backcount = 1;
- }
- }
-
- if (!frontcount)
- return SIDE_BACK;
- if (!backcount)
- return SIDE_FRONT;
-
- return SIDE_ON;
- }
-
- /*
- * ==================
- * ChooseMidPlaneFromList
- *
- * The clipping hull BSP doesn't worry about avoiding splits
- * ==================
- */
- struct surface *ChooseMidPlaneFromList(__memBase, register struct surface * surfaces, register vec3_t mins, register vec3_t maxs)
- {
- int j, l;
- struct surface *p, *bestsurface;
- vec_t bestvalue, value, dist;
- struct plane *plane;
-
- //
- // pick the plane that splits the least
- //
- bestvalue = 6 * 8192 * 8192;
- bestsurface = NULL;
-
- for (p = surfaces; p; p = p->next) {
- if (p->onnode)
- continue;
-
- #ifdef EXHAUSIVE_CHECK
- if(p->planenum >= bspMem->numbrushplanes || p->planenum < 0)
- Error("looking for nonexisting plane %d\n", p->planenum);
- #endif
- plane = &bspMem->brushplanes[p->planenum];
-
- // check for axis aligned surfaces
- l = plane->type;
- if (l > PLANE_Z)
- continue;
-
- //
- // calculate the split metric along axis l, smaller values are better
- //
- value = 0;
-
- dist = plane->dist * plane->normal[l];
- for (j = 0; j < 3; j++) {
- if (j == l) {
- value += (maxs[l] - dist) * (maxs[l] - dist);
- value += (dist - mins[l]) * (dist - mins[l]);
- }
- else
- value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
- }
-
- if (value > bestvalue)
- continue;
-
- //
- // currently the best!
- //
- bestvalue = value;
- bestsurface = p;
- }
-
- if (!bestsurface) {
- for (p = surfaces; p; p = p->next)
- if (!p->onnode)
- return p; // first valid surface
-
- Error("ChooseMidPlaneFromList: no valid planes");
- }
-
- return bestsurface;
- }
-
- /*
- * ==================
- * ChoosePlaneFromList
- *
- * The real BSP hueristic
- * ==================
- */
- struct surface *ChoosePlaneFromList(__memBase, register struct surface * surfaces, register vec3_t mins, register vec3_t maxs, register bool usefloors)
- {
- int j, k, l;
- struct surface *p, *p2, *bestsurface;
- vec_t bestvalue, bestdistribution, value, dist;
- struct plane *plane;
- struct visfacet *f;
-
- //
- // pick the plane that splits the least
- //
- bestvalue = 99999;
- bestsurface = NULL;
- bestdistribution = 9e30;
-
- for (p = surfaces; p; p = p->next) {
- if (p->onnode)
- continue;
-
- #ifdef EXHAUSIVE_CHECK
- if(p->planenum >= bspMem->numbrushplanes || p->planenum < 0)
- Error("looking for nonexisting plane %d\n", p->planenum);
- #endif
- plane = &bspMem->brushplanes[p->planenum];
- k = 0;
-
- if (!usefloors && plane->normal[2] == 1)
- continue;
-
- for (p2 = surfaces; p2; p2 = p2->next) {
- if (p2 == p)
- continue;
- if (p2->onnode)
- continue;
-
- for (f = p2->faces; f; f = f->next) {
- if (FaceSide(f, plane) == SIDE_ON) {
- k++;
- if (k >= bestvalue)
- break;
- }
-
- }
- if (k > bestvalue)
- break;
- }
-
- if (k > bestvalue)
- continue;
-
- // if equal numbers, axial planes win, then decide on spatial subdivision
-
- if (k < bestvalue || (k == bestvalue && plane->type < PLANE_ANYX)) {
- // check for axis aligned surfaces
- l = plane->type;
-
- if (l <= PLANE_Z) { // axial aligned
- //
- // calculate the split metric along axis l
- //
-
- value = 0;
-
- for (j = 0; j < 3; j++) {
- if (j == l) {
- dist = plane->dist * plane->normal[l];
- value += (maxs[l] - dist) * (maxs[l] - dist);
- value += (dist - mins[l]) * (dist - mins[l]);
- }
- else
- value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
- }
-
- if (value > bestdistribution && k == bestvalue)
- continue;
- bestdistribution = value;
- }
- //
- // currently the best!
- //
- bestvalue = k;
- bestsurface = p;
- }
-
- }
-
- return bestsurface;
- }
-
- /*
- * ==================
- * SelectPartition
- *
- * Selects a surface from a linked list of surfaces to split the group on
- * returns NULL if the surface list can not be divided any more (a leaf)
- * ==================
- */
- struct surface *SelectPartition(__memBase, register struct surface * surfaces)
- {
- int i;
- short int j;
- vec3_t mins, maxs;
- struct surface *p, *bestsurface;
-
- //
- // count onnode surfaces
- //
- i = 0;
- bestsurface = NULL;
- for (p = surfaces; p; p = p->next)
- if (!p->onnode) {
- i++;
- bestsurface = p;
- }
-
- if (i == 0)
- return NULL;
-
- if (i == 1)
- return bestsurface; // this is a final split
-
- //
- // calculate a bounding box of the entire surfaceset
- //
- for (i = 0; i < 3; i++) {
- mins[i] = 99999;
- maxs[i] = -99999;
- }
-
- for (p = surfaces; p; p = p->next)
- for (j = 0; j < 3; j++) {
- if (p->mins[j] < mins[j])
- mins[j] = p->mins[j];
- if (p->maxs[j] > maxs[j])
- maxs[j] = p->maxs[j];
- }
-
- if (usemidsplit) // do fast way for clipping hull
-
- return ChooseMidPlaneFromList(bspMem, surfaces, mins, maxs);
-
- // do slow way to save poly splits for drawing hull
- #if 0
- bestsurface = ChoosePlaneFromList(surfaces, mins, maxs, FALSE);
- if (bestsurface)
- return bestsurface;
- #endif
- return ChoosePlaneFromList(bspMem, surfaces, mins, maxs, TRUE);
- }
-
- //============================================================================
-
- /*
- * =================
- * CalcSurfaceInfo
- *
- * Calculates the bounding box
- * =================
- */
- void CalcSurfaceInfo(register struct surface * surf)
- {
- int i;
- short int j;
- struct visfacet *f;
-
- if (!surf->faces)
- Error("CalcSurfaceInfo: surface without a face");
-
- //
- // calculate a bounding box
- //
- for (i = 0; i < 3; i++) {
- surf->mins[i] = 99999;
- surf->maxs[i] = -99999;
- }
-
- for (f = surf->faces; f; f = f->next) {
- if (f->contents[0] >= 0 || f->contents[1] >= 0)
- Error("Bad contents");
- for (i = 0; i < f->numpoints; i++)
- for (j = 0; j < 3; j++) {
- if (f->pts[i][j] < surf->mins[j])
- surf->mins[j] = f->pts[i][j];
- if (f->pts[i][j] > surf->maxs[j])
- surf->maxs[j] = f->pts[i][j];
- }
- }
- }
-
- /*
- * ==================
- * DividePlane
- * ==================
- */
- void DividePlane(__memBase, register struct surface * in, register struct plane * split, register struct surface ** front, register struct surface ** back)
- {
- struct visfacet *facet, *next;
- struct visfacet *frontlist, *backlist;
- struct visfacet *frontfrag, *backfrag;
- struct surface *news;
- struct plane *inplane;
-
- #ifdef EXHAUSIVE_CHECK
- if(inplane->planenum >= bspMem->numbrushplanes || inplane->planenum < 0)
- Error("looking for nonexisting plane %d\n", inplane->planenum);
- #endif
- inplane = &bspMem->brushplanes[in->planenum];
-
- // parallel case is easy
- if (VectorCompare(inplane->normal, split->normal)) {
- // check for exactly on node
- if (inplane->dist == split->dist) { // divide the facets to the front and back sides
-
- news = AllocSurface();
- *news = *in;
-
- facet = in->faces;
- in->faces = NULL;
- news->faces = NULL;
- in->onnode = news->onnode = TRUE;
-
- for (; facet; facet = next) {
- next = facet->next;
- if (facet->planeside == 1) {
- facet->next = news->faces;
- news->faces = facet;
- }
- else {
- facet->next = in->faces;
- in->faces = facet;
- }
- }
-
- if (in->faces)
- *front = in;
- else
- *front = NULL;
- if (news->faces)
- *back = news;
- else
- *back = NULL;
- return;
- }
-
- if (inplane->dist > split->dist) {
- *front = in;
- *back = NULL;
- }
- else {
- *front = NULL;
- *back = in;
- }
- return;
- }
-
- // do a real split. may still end up entirely on one side
- // OPTIMIZE: use bounding box for fast test
- frontlist = NULL;
- backlist = NULL;
-
- for (facet = in->faces; facet; facet = next) {
- next = facet->next;
- SplitFace(facet, split, &frontfrag, &backfrag);
- if (frontfrag) {
- frontfrag->next = frontlist;
- frontlist = frontfrag;
- }
- if (backfrag) {
- backfrag->next = backlist;
- backlist = backfrag;
- }
- }
-
- // if nothing actually got split, just move the in plane
-
- if (frontlist == NULL) {
- *front = NULL;
- *back = in;
- in->faces = backlist;
- return;
- }
-
- if (backlist == NULL) {
- *front = in;
- *back = NULL;
- in->faces = frontlist;
- return;
- }
-
- // stuff got split, so allocate one new plane and reuse in
- news = AllocSurface();
- *news = *in;
- news->faces = backlist;
- *back = news;
-
- in->faces = frontlist;
- *front = in;
-
- // recalc bboxes and flags
- CalcSurfaceInfo(news);
- CalcSurfaceInfo(in);
- }
-
- /*
- * ==================
- * DivideNodeBounds
- * ==================
- */
- void DivideNodeBounds(register struct node * node, register struct plane * split)
- {
- VectorCopy(node->mins, node->children[0]->mins);
- VectorCopy(node->mins, node->children[1]->mins);
- VectorCopy(node->maxs, node->children[0]->maxs);
- VectorCopy(node->maxs, node->children[1]->maxs);
-
- // OPTIMIZE: sloping cuts can give a better bbox than this...
- if (split->type > 2)
- return;
-
- node->children[0]->mins[split->type] =
- node->children[1]->maxs[split->type] = split->dist;
- }
-
- /*
- * ==================
- * LinkConvexFaces
- *
- * Determines the contents of the leaf and creates the final list of
- * original faces that have some fragment inside this leaf
- * ==================
- */
- void LinkConvexFaces(register struct surface * planelist, register struct node * leafnode)
- {
- struct visfacet *f, *next;
- struct surface *surf, *pnext;
- int i, count;
-
- leafnode->faces = NULL;
- leafnode->contents = 0;
- leafnode->planenum = -1;
-
- count = 0;
- for (surf = planelist; surf; surf = surf->next) {
- for (f = surf->faces; f; f = f->next) {
- count++;
- if (!leafnode->contents)
- leafnode->contents = f->contents[0];
- else if (leafnode->contents != f->contents[0])
- Error("Mixed face contents in leafnode");
- }
- }
-
- if (!leafnode->contents)
- leafnode->contents = CONTENTS_SOLID;
-
- switch (leafnode->contents) {
- case CONTENTS_EMPTY:
- c_empty++;
- break;
- case CONTENTS_SOLID:
- c_solid++;
- break;
- case CONTENTS_WATER:
- case CONTENTS_SLIME:
- case CONTENTS_LAVA:
- case CONTENTS_SKY:
- c_water++;
- break;
- default:
- Error("LinkConvexFaces: bad contents number");
- }
-
- //
- // write the list of faces, and free the originals
- //
- leaffaces += count;
- if(!(leafnode->markfaces = (struct visfacet **)tmalloc(sizeof(struct visfacet *) * (count + 1))))
- Error("LinkConvexFaces: failed to allocate markfaces\n");
- i = 0;
- for (surf = planelist; surf; surf = pnext) {
- pnext = surf->next;
- for (f = surf->faces; f; f = next) {
- next = f->next;
- leafnode->markfaces[i] = f->original;
- i++;
- FreeFace(f);
- }
- FreeSurface(surf);
- }
- leafnode->markfaces[i] = NULL; // sentinal
-
- }
-
- /*
- * ==================
- * LinkNodeFaces
- *
- * Returns a duplicated list of all faces on surface
- * ==================
- */
- struct visfacet *LinkNodeFaces(__memBase, register struct surface * surface)
- {
- struct visfacet *f, *new, **prevptr;
- struct visfacet *list;
-
- list = NULL;
-
- // subdivide
- prevptr = &surface->faces;
- while (1) {
- f = *prevptr;
- if (!f)
- break;
- SubdivideFace(bspMem, f, prevptr);
- f = *prevptr;
- prevptr = &f->next;
- }
-
- // copy
- for (f = surface->faces; f; f = f->next) {
- nodefaces++;
- new = AllocFace(f->numpoints);
- CopyFace(new, f);
- f->original = new;
- new->next = list;
- list = new;
- }
-
- return list;
- }
-
- /*
- * ==================
- * PartitionSurfaces
- * ==================
- */
- void PartitionSurfaces(__memBase, register struct surface * surfaces, register struct node * node)
- {
- struct surface *split, *p, *next;
- struct surface *frontlist, *backlist;
- struct surface *frontfrag, *backfrag;
- struct plane *splitplane;
-
- split = SelectPartition(bspMem, surfaces);
- if (!split) { // this is a leaf node
-
- node->planenum = PLANENUM_LEAF;
- LinkConvexFaces(surfaces, node);
- return;
- }
-
- splitnodes++;
- node->faces = LinkNodeFaces(bspMem, split);
- node->children[0] = AllocNode();
- node->children[1] = AllocNode();
- node->planenum = split->planenum;
-
- #ifdef EXHAUSIVE_CHECK
- if(split->planenum >= bspMem->numbrushplanes || split->planenum < 0)
- Error("looking for nonexisting plane %d\n", split->planenum);
- #endif
- splitplane = &bspMem->brushplanes[split->planenum];
-
- DivideNodeBounds(node, splitplane);
-
- //
- // multiple surfaces, so split all the polysurfaces into front and back lists
- //
- frontlist = NULL;
- backlist = NULL;
-
- for (p = surfaces; p; p = next) {
- next = p->next;
- DividePlane(bspMem, p, splitplane, &frontfrag, &backfrag);
- if (frontfrag && backfrag) {
- // the plane was split, which may expose oportunities to merge
- // adjacent faces into a single face
- // MergePlaneFaces (frontfrag);
- // MergePlaneFaces (backfrag);
- }
-
- if (frontfrag) {
- if (!frontfrag->faces)
- Error("surface with no faces");
- frontfrag->next = frontlist;
- frontlist = frontfrag;
- }
- if (backfrag) {
- if (!backfrag->faces)
- Error("surface with no faces");
- backfrag->next = backlist;
- backlist = backfrag;
- }
- }
-
- PartitionSurfaces(bspMem, frontlist, node->children[0]);
- PartitionSurfaces(bspMem, backlist, node->children[1]);
- }
-
- /*
- * ==================
- * DrawSurface
- * ==================
- */
- void DrawSurface(register struct surface * surf)
- {
- struct visfacet *f;
-
- for (f = surf->faces; f; f = f->next)
- Draw_DrawFace(f);
- }
-
- /*
- * ==================
- * DrawSurfaceList
- * ==================
- */
- void DrawSurfaceList(register struct surface * surf)
- {
- Draw_ClearWindow();
- while (surf) {
- DrawSurface(surf);
- surf = surf->next;
- }
- }
-
- /*
- * ==================
- * SolidBSP
- * ==================
- */
- struct node *SolidBSP(__memBase, struct surface * surfhead, bool midsplit)
- {
- short int i;
- struct node *headnode;
-
- mprintf("----- SolidBSP ----------\n");
-
- headnode = AllocNode();
- usemidsplit = midsplit;
-
- //
- // calculate a bounding box for the entire model
- //
- for (i = 0; i < 3; i++) {
- headnode->mins[i] = brushset->mins[i] - SIDESPACE;
- headnode->maxs[i] = brushset->maxs[i] + SIDESPACE;
- }
-
- //
- // recursively partition everything
- //
- Draw_ClearWindow();
- splitnodes = 0;
- leaffaces = 0;
- nodefaces = 0;
- c_solid = c_empty = c_water = 0;
-
- PartitionSurfaces(bspMem, surfhead, headnode);
-
- mprintf("%5i split nodes\n", splitnodes);
- mprintf("%5i solid leafs\n", c_solid);
- mprintf("%5i empty leafs\n", c_empty);
- mprintf("%5i water leafs\n", c_water);
- mprintf("%5i leaffaces\n", leaffaces);
- mprintf("%5i nodefaces\n", nodefaces);
-
- return headnode;
- }
-